package com.incross.mobiletrackinglib;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.http.HttpResponse;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.os.Environment;
import android.util.Log;
import android.webkit.WebView;
import android.widget.Toast;

import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.incross.mobiletrackinglib.common.MobileTrackingConfig;
import com.incross.mobiletrackinglib.http.BKConnectionManager;
import com.incross.mobiletrackinglib.tracking.TrackingInformations;
import com.incross.mobiletrackinglib.util.BKFileManager;
import com.incross.mobiletrackinglib.util.BKLog;
import com.incross.mobiletrackinglib.util.CommonUtils;


public class MobileTracking implements BKConnectionManager.RequestStateListener {
	
	// for purchase type
	public static final int PURCHASE_MONEY  = 100;
	public static final int PURCHASE_POINT  = 101;
	public static final int PURCHASE_COUPON = 102;
	public static final int PURCHASE_ETC    = 103;
	
	
	// request codes
	public static final int REQUEST_APP_START    = 200;
	public static final int REQUEST_APP_FINISH   = 201;
	public static final int REQUEST_PURCHASE     = 202;
	public static final int REQUEST_PAGE_CHANGE  = 203;
	public static final int REQUEST_WEB_TRACKING = 204;
	
	// for mode selection
	public static final int TRACKING_SERVER_TYPE_DEV = 210;
	public static final int TRACKING_SERVER_TYPE_REL = 211;
	
	// key for save install referrer.
	public static final String KEY_INSTALL_REFERRER = "key_install_referrer";
	
	private int mTrackingServerType = TRACKING_SERVER_TYPE_DEV;
	private static final String SAVE_FILE_PATH = Environment.getExternalStorageDirectory() + "/android/data/incross/mat/";
	private static final String SAVE_FILE_NAME = "incross_adp_mat.txt";
	
	private static final String KEY_AD_ID = "key_ad_id";
	private static final String KEY_UDT_ID = "key_udt_id";
	
	// mWebTrackingPool and mWebViewPool must have same size.
	private ArrayList<String> mWebTrackingPool;
	private ArrayList<WebView> mWebViewPool;
	
	// base url
	private final String mBaseUrl = "";
	private final String mBaseUrl_Dev = /*"http://112.169.59.43:8081/"*/"https://adpdev.kr.pe:9081/";
	
	// App ID 는 DEV mode에서만 변경이 가능함.
	private String mAppId = "";
	
	private Context mContext;
	private TrackingInformations mTrackingInfo;
	
	private BKConnectionManager mConnManager;
	
	private static MobileTracking mMobileTrackingInstance;
	
	private boolean mIsAppActivating = false;
	private boolean mIsStartTagSended = false;
	
	private ArrayList<String> mPendingRequests;
	
	private long mStartTimeSec = 0;
	private long mEndTimeSec = 0;
	
	private String mSessionId = "UNKNOWN";
	private String mUdtId = "UNKNOWN";
	private String mAdId = "UNKNOWN";
	
	// for check Activity life cycle
	private boolean mIsOnStartCalled = false;
	private boolean mIsOnResumeCalled = false;
	private boolean mIsOnPauseCalled = false;
	
	private AdvertisingIdClient.Info mAdInfo;
	
	public static MobileTracking getInstances(Context context, String appId) {
		if(context == null) return null;
		if(mMobileTrackingInstance == null) {
			mMobileTrackingInstance = new MobileTracking(context, appId);
			BKLog.i("Create new mobile tracking instance");
		} else {
			BKLog.i("Mobile tracking instance already created!!");
		}

		return mMobileTrackingInstance;
	}
	
	private MobileTracking(Context context, String appId) {
		mContext = context;
		mAppId = appId;
		mTrackingInfo = new TrackingInformations(context);
		mConnManager = new BKConnectionManager(context);
		mConnManager.setOnConnectionStateListener(this);
		mPendingRequests = new ArrayList<String>();
		initializeWebTrackingPool();
		initializeVariables();
		
		String udtId = getUdtId();
		if(udtId != null && !udtId.equals("") && !udtId.equals("")) {
			mUdtId = udtId;
		}
		
		String adId = getAdId();
		if(adId != null && !adId.equals("")) {
			mAdId = adId;
		}
		
		BKLog.i("--------------- App ID : " + appId);
		BKLog.i("--------------- UDT ID : " + mUdtId);
		BKLog.i("--------------- AD ID : " + mAdId);
		
	}
	
	private void initializeWebTrackingPool() {
		if(mWebTrackingPool == null) {
			mWebTrackingPool = new ArrayList<String>();
		}
		
		if(mWebViewPool == null) {
			mWebViewPool = new ArrayList<WebView>();
		}
	}
	
	private void extractAdvertisingId() {
		new Thread() {
			@Override
			public void run() {
				try {
					mAdInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);
					if(mAdInfo != null) {
						if(mAdInfo.isLimitAdTrackingEnabled()) {
							BKLog.e("isLimitAdTrackingEnabled checked");
						} else {
							BKLog.e("isLimitAdTrackingEnabled unchecked");
						}
						// TODO : AD ID preference로 저장해서 사용.
						String adId = mAdInfo.getId();
						
						if(adId != null && mAdId != null) {
							if(!adId.equals("") && !mAdId.equals(adId)) {
								BKLog.e("Save AD ID : " + mAdInfo.getId());
								CommonUtils.setStringPreference(mContext, KEY_AD_ID, adId);
							} else {
								BKLog.i("Saved AD ID is same as just extracted : " + adId);
							}
						} else {
							BKLog.i("received adid or loaded adid is null");
						}
					}
				} catch(IllegalStateException e) {
					e.printStackTrace();
				} catch(GooglePlayServicesRepairableException e) {
					e.printStackTrace();
				} catch(IOException e) {
					e.printStackTrace();
				} catch(GooglePlayServicesNotAvailableException e) {
					e.printStackTrace();
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}.start();
	}
	
	/*
	 * Lifecycle 관련 class 정의
	 * 정의된 Lifecycle을 호출해야 정상적인 트래킹 가능함.
	 * */
	public void onStart() {
		if(mIsAppActivating) { // 앱이 처음 시작하는지의 여부를 판단함. onResume에서 App activating check를 하므로 여기서 해당 flag를 체크하면 새로운 세션인지 여부 판단가능.
			mIsOnStartCalled = true;
			if(mIsOnPauseCalled) {
				mIsOnPauseCalled = false;
				BKLog.i("Maybe show other activity or back from new activity.");
			}
		} else {
			BKLog.i("New application session started!");
		}
	}
	
	// only use when debug mode.
	public void setAppId(String appId) {
		mAppId = "";
		if(appId != null && !appId.equals("") && MobileTrackingConfig.isDebugMode()) {
			mAppId = appId;
			BKLog.w("User inserted app id set. (" + mAppId + ")");
		}
	}
	
	public void onResume() {
		// TODO : onResume과 onPause는 한 세트로 생각을 해 봅시다.
		mIsOnResumeCalled = true;
		
		if(!mIsAppActivating) {
			// Application이 처음 구동된다고 판단한다.
			mStartTimeSec = System.currentTimeMillis() / 1000;
			mIsAppActivating = true;
			sendStartInfo();
			// TODO : App 시작 태그 보냄. 시작태그보내고 받은 결과값의 일회성 uniq ID를 추출한 후 다음번 모든 요청에 추가를 한다.
			
		} else {
			BKLog.i("Application is arleady activated.");
		}
		
	}
	
	public void onPause() {
		mIsOnPauseCalled = true;
		// TODO : onResume과 세트로 사용함. onPause 호출 후 onResume이 500msec 내에 들어오지 않는다면 종료로 처리함.
	}
	
	public void onStop() {
		// onStop이 호출됐을 때, 생명주기의 onResume까지 호출이 되었다면, 새로운 Activity 호출로 간주하고 계속 실행중으로 판단한다.
		if(mIsOnStartCalled && mIsOnResumeCalled) {
			mIsAppActivating = true;
			// mIsAppActivating 으로 App active를 판단하고, 생명주기 체크용으로 만든 변수를 초기값 false로 세팅한다.
			mIsOnStartCalled = false;
			mIsOnResumeCalled = false;
			BKLog.d("Maybe new activity called");
		} else {
			// 종료로 판단하는 경우. 
			// 앱을 이탈하는 모든 경우를 종료로 생각한다.
			mEndTimeSec = System.currentTimeMillis() / 1000;
			finishApplication();
			BKLog.d("Finish app");
		}
	}
	
	public void onDestroy() {
		// TODO : Activating 상태가 false인데
	}
	
	public void onConfigurationChanged() {
		// TODO : 혹시 있을지 모를 가로/세로 전환시 onCreate를 새로 부르는 곳이 있을지 모르므로..
		
	}
	
	
	private void finishApplication() {
		BKLog.i("Application finish - ");
		sendEndInfo();
		// 모든것을 초기화함.
		initializeVariables();
		mMobileTrackingInstance = null;
	}
	
	private void initializeVariables() {
		// 변수 초기화
		mIsOnStartCalled = false;
		mIsOnPauseCalled = false;
		mIsOnResumeCalled = false;
		mIsAppActivating = false;
		mIsStartTagSended = false;
		mStartTimeSec = 0;
		mEndTimeSec = 0;
		mAppId = "";
		if(mPendingRequests != null) {
			mPendingRequests.clear();
		}
	}
	
	/*
	 * other methods
	 * 
	 * */
	/**
	 * set server type. default server type is MobileTracking.TRACKING_SERVER_TYPE_DEV
	 * @param type : one of specified server type in MobileTracking class
	 * @return -1 if put wrong server type. 0 success to change
	 */
	public int setTrackingServerType(int type) {
		if(type == TRACKING_SERVER_TYPE_DEV || type == TRACKING_SERVER_TYPE_REL) {
			mTrackingServerType = type;
			return 0;
		}
		return -1;
	}
	
	private String getBaseUrl(int requestType) {
		String baseUrl = "";
		
		if(mTrackingServerType == TRACKING_SERVER_TYPE_REL) {
			baseUrl = mBaseUrl;
		} else {
			baseUrl = mBaseUrl_Dev;
		}
		
		switch(requestType) {
			case REQUEST_APP_START:
				baseUrl += "start";
				break;
				
			case REQUEST_PAGE_CHANGE:
				baseUrl += "page";
				break;
				
			case REQUEST_PURCHASE:
				baseUrl += "purchase";
				break;
				
			case REQUEST_APP_FINISH:
				baseUrl += "end";
				break;
				
			case REQUEST_WEB_TRACKING: // 따로 정의된 바가 없으므로 pageTracking과 동일 취급
				baseUrl += "page";
				break;
		}
		return baseUrl;
	}
	
	private void sendPendingRequest() {
		if(mPendingRequests != null && mPendingRequests.size() > 0) {
			for(int i = 0; i < mPendingRequests.size(); i++) {
				String url = mPendingRequests.get(i);
				int request = extractRequest(url);
				if(request != -1) {
					BKLog.w("send pending request : " + mPendingRequests.get(i));
					mConnManager.sendRequest(request, checkUnknownValues(mPendingRequests.get(i)), BKConnectionManager.HTTP_POST);
				}
			}
		}
	}
	
	private int extractRequest(String url) {
		int request = -1;
		if(url != null && !url.equals("")) {
			String[] splitString = url.split("\\?");
			String baseUrl = "";
			if(splitString != null && splitString.length != 0) {
				baseUrl = splitString[0];
				String[] splitString2 = baseUrl.split("\\/");
				
				if(splitString2 != null && splitString2.length != 0) {
					String target = splitString2[splitString2.length - 1];
					
					if(target.equals("page")) {
						request = REQUEST_PAGE_CHANGE;
					} else if(target.equals("purchase")) {
						request = REQUEST_PURCHASE;
					}
				}
			}
		}
		return request;
	}
	
	private String checkUnknownValues(String url) {
		String result = "";
		ArrayList<String> splitList = new ArrayList<String>();
		boolean isUnknownSsidFound = false;
		boolean isUnknownAdidFound = false;
		boolean isUnknownUdtidFound = false;
		
		try {
			if(url != null && !url.equals("")) {
				String[] splitString = url.split("\\&");
				if(splitString != null && splitString.length != 0) {
					// 1. arraylist에 split한 string 추가.. 2. arraylist 체크하여 session id가 포함된 부분이 있는지 체크 3. session id와 = 을 구분하여 unknown여부 확인 4. unknown이면 저장된 놈과 바꾸어 조립
					for(int i = 0; i < splitString.length; i++) {
						BKLog.d(i + ") put into ArrayList : " + splitString[i]);
						splitList.add(splitString[i]);
					}
					
					for(int i = 0; i < splitList.size(); i++) {
						String target = (String) splitList.get(i);
						if(target.contains("session_id")) {
							String[] spTarget = target.split("\\=");
							if(spTarget != null && spTarget.length > 0) {
								String sessionId = spTarget[1];
								if(!sessionId.equals(mSessionId)) {
									BKLog.d("---------- unknown session id param found");
									splitList.remove(i);
									splitList.add("session_id=" + mSessionId);
									isUnknownSsidFound = true;
									BKLog.i("unknown session id replaced : " + mSessionId);
								}
							}
						}
						
						if(target.contains("ad_id")) {
							String[] spTarget = target.split("\\=");
							if(spTarget != null && spTarget.length > 0) {
								String adId = spTarget[1];
								String savedAdId = CommonUtils.getStringPreference(mContext, KEY_AD_ID);
								if(adId != null && savedAdId != null && !savedAdId.equals("") && (adId.equals("UNKNOWN") || adId.equals(""))) {
									splitList.remove(i);
									splitList.add("ad_id=" + savedAdId);
									isUnknownAdidFound = true;
									BKLog.i("unknown ad id replaced : " + savedAdId);
								}
							}
						}
						
						if(target.contains("udt_id")) {
							String[] spTarget = target.split("\\=");
							if(spTarget != null && spTarget.length > 0) {
								String udtId = spTarget[1];
								BKLog.i("unknown udt id replaced : " + mUdtId);
								if(udtId != null && mUdtId != null && !mUdtId.equals("UNKNOWN") && (udtId.equals("UNKNOWN") || udtId.equals(""))) {
									splitList.remove(i);
									splitList.add("udt_id=" + mUdtId);
									isUnknownUdtidFound = true;
									BKLog.i("unknown udt id replaced : " + mUdtId);
								}
								
							}
						}
					}
					
					if(isUnknownSsidFound || isUnknownAdidFound || isUnknownUdtidFound) {
						for(int i = 0; i < splitList.size(); i++) {
							result += splitList.get(i) + (i != splitList.size() - 1? "&": "");
						}
					} else {
						result = url;
					}
					BKLog.i("result : " + result);
				}
			}
		} catch(Exception e) {
			e.printStackTrace();
			result = url;
		}
		
		return result;
	}
	
	private boolean saveUdtId(String udtId) {
		if(udtId == null || udtId.equals("") || udtId.equals("UNKNOWN")) return false;
		
		BKFileManager fManager = new BKFileManager();
		
		try {
			JSONObject obj = new JSONObject();
			obj.put(KEY_UDT_ID, udtId);
			
			String jsonString = obj.toString();
			if(fManager.saveToTextFile(SAVE_FILE_PATH, SAVE_FILE_NAME, jsonString)) {
				BKLog.i("Save UDT ID : " + jsonString);
				return true;
			} else {
				return false;
			}
		} catch(JSONException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	private String getUdtId() {
		String fileString = "";
		String result = "";
		
		BKFileManager fManager = new BKFileManager();
		BKLog.i("File Path : " + SAVE_FILE_PATH + SAVE_FILE_NAME);
		fileString = fManager.readTextFileToString(SAVE_FILE_PATH + SAVE_FILE_NAME);
		
		if(fileString == null || fileString.equals("")) {
			BKLog.w("Saved uniq id not found");
			fileString = "";
		} else {
			BKLog.e("saved file String : " + fileString);
		}
		
		try {
			JSONObject obj = new JSONObject(fileString);
			result = obj.getString(KEY_UDT_ID);
		} catch(JSONException e) {
			result = "";
			e.printStackTrace();
		}
		
		return result;
	}
	
	private String getAdId() {
		// 2014-07-29 Preference에 저장된 AdId가져오기를 시도한 후 없으면
		String adId = CommonUtils.getStringPreference(mContext, KEY_AD_ID);
		extractAdvertisingId();
		return adId;
	}
	
	/*
	 * Action 관련 class 정의
	 * 모든 요청에는 서버에서 발급받은 Session ID, package name, udid 를 넣어서 보내는 것으로..
	 * */
	
	/**
	 * Call this when any case of pay.
	 * @param productId : product's identification.
	 */
	public void sendPurchaseInfo(String productId, String price) {
		if(productId == null || productId.equals("")) {
			productId = "UNKNOWN";
		}
		
		if(price == null || price.equals("")) {
			price = "UNKNOWN";
		}
		
		String url = addDefaultParam(REQUEST_PURCHASE);
		
		url = url + "&product_id=" + productId + "&product_price=" + price;
		// 20140624 서버팀 요청으로 구매시 유입경로, 국가, 디바이스네임, 통신사 추가하는 것으로 수정
		if(mTrackingInfo != null) {
			String referrer = CommonUtils.getStringPreference(mContext, KEY_INSTALL_REFERRER);
			url += "&referrer=" + (referrer == null || referrer.equals("")? "UNKNOWN" : referrer) + "&country=" + mTrackingInfo.getCountryCode() + 
				"&device_name=" + mTrackingInfo.getDeviceName() + "&provider=" + mTrackingInfo.getServiceProvider();
		}
		
		if(mConnManager == null) {
			mConnManager = new BKConnectionManager(mContext);
			mConnManager.setOnConnectionStateListener(this);
		}
		if(mIsStartTagSended) {
			mConnManager.sendRequest(REQUEST_PURCHASE, url, BKConnectionManager.HTTP_POST);
		} else {
			if(mPendingRequests == null) {
				mPendingRequests = new ArrayList<String>();
			}
			mPendingRequests.add(url);
		}
		
	}
	
	public void sendPageInfo(String pageId) {
		// TODO : page별 트래킹용으로 페이지가 변경될 때 또는 특정 Activity별 방문경로 추적시에 넣는다.
		String url = addDefaultParam(REQUEST_PAGE_CHANGE);
		url += "&page_id=" + pageId;
		if(mIsStartTagSended) {
			mConnManager.sendRequest(REQUEST_PAGE_CHANGE, url, BKConnectionManager.HTTP_POST);
			Toast.makeText(mContext, "Page tracking tag 전송.(PageId : " + pageId + ")", Toast.LENGTH_SHORT).show();
		} else {
			mPendingRequests.add(url);
			BKLog.d("pending page info send - start tag not sended");
		}
		
	}
	
	/**
	 * call on shouldOverrideUrlLoading(WebView view, String url)
	 * @param webView 
	 * @param url
	 */
	public void sendWebTrackInfo(WebView webView, String url) {
		// TODO : 리다이렉션으로 인해 중복으로 들어가는 url에 대한 처리 추가.. 어떻게 할 것인가...
		// 1. 풀의 초기화 여부 확인 2. 풀이 비어있는지 여부 확인 3. 풀이 비어있으면 삽입 후 100msec뒤에 전송하는 것으로 
		if(webView == null || url == null) return;
		BKLog.e("################## sendWebTrackingInfo called ########################");
		Log.e("BenTag", "WebView url = " + url);
		BKLog.e("Original Url : " + webView.getOriginalUrl());
		
		String encodedUrl = "";
		try {
			encodedUrl = URLEncoder.encode(url, "UTF-8");
			BKLog.e("encoded url : " + encodedUrl);
		} catch(Exception e) {
			e.printStackTrace();
		}
		
		// step 1 풀의 초기화 여부 확인
		if(mWebTrackingPool == null || mWebViewPool == null) initializeWebTrackingPool();
		
		boolean isSameOriginalUrlSaved = false; // WebView
		// step 2 풀이 비어있는지 확인하여 풀이 비어있으면 바로 넣고 아니면 비교
		if(mWebTrackingPool.size() == 0 && mWebViewPool.size() == 0) {
			BKLog.d("- Pool is empty. save web tracking information into web tracking pool");
			mWebTrackingPool.add(encodedUrl);
			mWebViewPool.add(webView);
		} else {
			// step 3 풀이 비어있지 않는 경우 real 주소 비교.
			BKLog.d("- Pool is Not empty. Check start whether same original url saved or not.");
			for(int i = 0; i < mWebViewPool.size(); i++) {
				WebView wv = (WebView) mWebViewPool.get(i);
				if(wv.getOriginalUrl().equals(webView.getOriginalUrl())) {
					isSameOriginalUrlSaved = true;
					break;
				}
			}
			BKLog.d("- pool check end");
			
			if(!isSameOriginalUrlSaved) {
				BKLog.d("- Not found same saved value.. add into pool.");
				mWebTrackingPool.add(encodedUrl);
				mWebViewPool.add(webView);
			}
		}
		
		// step 4. 풀의 사이즈를 보고, 풀이 비어있지 않으며, 동일 original url을 발견하지 않은경우
		if(!isSameOriginalUrlSaved && mWebTrackingPool.size() != 0) {
			BKLog.d("Make timer task. and schedule with 300msec delay. - " + mWebTrackingPool.get(0));
			Timer timer = new Timer();
			TimerTask task = new TimerTask() {

				@Override
				public void run() {
					BKLog.d("300msec passed. prepare to send web page tracking.");
					String webTracking = mWebTrackingPool.get(0);
					if(webTracking != null) {
						BKLog.d("Send Web tracking info. - " + webTracking);
						String url = addDefaultParam(REQUEST_WEB_TRACKING) + "&page_id=" + webTracking;
						if(mIsStartTagSended) {
							mConnManager.sendRequest(REQUEST_WEB_TRACKING, url, BKConnectionManager.HTTP_POST);
						} else {
							if(mPendingRequests == null) {
								mPendingRequests = new ArrayList<String>();
							}
							mPendingRequests.add(url);
						}
						
						//////////////////////////////////////////////////////
						BKLog.e("Pool before remove.");
						for(int i = 0; i < mWebTrackingPool.size(); i++) {
							BKLog.d(i + " pool : " + mWebTrackingPool.get(i));
						}
						//////////////////////////////////////////////////////
						// remove pool
						mWebTrackingPool.remove(0);
						mWebViewPool.remove(0);
						
						//////////////////////////////////////////////////////
						BKLog.e("Pool after remove.");
						for(int i = 0; i < mWebTrackingPool.size(); i++) {
							BKLog.d(i + " pool : " + mWebTrackingPool.get(i));
						}
						//////////////////////////////////////////////////////
					} else {
						BKLog.e("saved value is null - wrong case ");
					}
				}
			};
			timer.schedule(task, 300);
		} else {
			if(isSameOriginalUrlSaved) {
				BKLog.i("Same original url is saved. do nothing..");
			}
			
			if(mWebTrackingPool.size() == 0) {
				BKLog.e("Pool size is zero");
			}
		}
	}
	
	private void sendStartInfo() {
		// TODO : 결과값으로 Session token 값을 받고 동일한 세션에서는 동일환 토큰을 사용하는 것으로 한다.
//		String url = addDefaultParam(REQUEST_APP_START, start);
		
		String url = addDefaultParam(REQUEST_APP_START);
		String installReferrer = CommonUtils.getStringPreference(mContext, KEY_INSTALL_REFERRER);
		
		// add device name, package name, vendor, location, start time second, gmt offset
		url = url + "&device_name=" + mTrackingInfo.getDeviceName() + "&package_name=" + mTrackingInfo.getPackageName() + "&provider=" + mTrackingInfo.getServiceProvider()
			+ "&country=" + mTrackingInfo.getCountryCode() + "&start_time=" + String.valueOf(mStartTimeSec) + "&gmt_offset=" + CommonUtils.getCurrentGmt() + "&content_type=json";
			
		
		if(installReferrer != null && !installReferrer.equals("")) {
			url = url + "&install_referrer=" + installReferrer;
			BKLog.i("Saved install referrer available : " + installReferrer);
		}
		BKLog.d("Send Start Tag : " + url);
		mConnManager.sendRequest(REQUEST_APP_START, url, BKConnectionManager.HTTP_POST);
	}
	
	private void sendEndInfo() {
		// TODO : 끝날 때 보낼 내용, 끝나는 시간의 현재 time sec, play time 서버에 전송
		double playTime = (mEndTimeSec - mStartTimeSec);
		String url = addDefaultParam(REQUEST_APP_FINISH);
		
		// 1. end time second 추가, 2. play time 추가 3. GMT offset 추가
		url = url + "&end_time=" + String.valueOf(mEndTimeSec) + "&play_time=" + String.valueOf(playTime) + "&gmt_offset=" + CommonUtils.getCurrentGmt();
		
		BKLog.d("Send End Tag : " + url);
		mConnManager.sendRequest(REQUEST_APP_FINISH, url, BKConnectionManager.HTTP_POST);
	}
	
	private String addDefaultParam(int request) {
		// 모든 Tag에 매번 포함되는 parameter를 분여준다.
		// 1. app id 2. device id(UDID) 3. App version 4. OS version 5. network env(wifi or 3g) 6. IP
		// 신규추가(20140729) 7. advertising id 8. uniq device tracking id(udt id)
		String url = getBaseUrl(request);
		if(mTrackingInfo != null) {
			boolean hasParameter = url.contains("?");
			
			if(!hasParameter) {
				url = url + "?";
			} else {
				url = url + "&";
			}
			String appId = "";
			if(mAppId == null || mAppId.equals("")) {
				appId = "UNKNOWN";
			} else {
				appId = mAppId;
			}
			
			// 1. Add appID, udid, app version, os version, network state, ip, ad id, udt id
			url = url + "app_id=" + appId + "&udid=" + mTrackingInfo.getUdid() + "&app_ver=" + mTrackingInfo.getAppVersion() + 
				"&os_ver=" + mTrackingInfo.getAndroidVersion() + "&network_state=" + mTrackingInfo.getNetworkState() + "&ip=" + mTrackingInfo.getIP() +
				"&ad_id=" + mAdId + "&udt_id=" + mUdtId;
			
			
			if(request != REQUEST_APP_START) {
				url = url + "&session_id=" + mSessionId;
			}
		}
		return url;
	}
	
	/**
	 * Request state listener
	 */
	
	@Override
	public void onRequestFinished(int whichRequest, HttpResponse result, String extractString) {
		switch(whichRequest) {
			case REQUEST_APP_START:
				Toast.makeText(mContext, "App start tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("start tag send success");
				try {
					JSONObject responseString = new JSONObject(extractString);
					JSONObject response = responseString.getJSONObject("response");
					mSessionId = response.getString("session_id");
					BKLog.i("-------- received session id : " + mSessionId);
					if(mUdtId.equals("") || mUdtId.equals("UNKNOWN")) {
						String udtId = response.getString("udt_id");
						if(udtId != null && !udtId.equals("")) {
							mUdtId = udtId;
							saveUdtId(mUdtId);
						}
						
						BKLog.i("-------- received udt id : " + mUdtId);
					}
					
					
				} catch(JSONException e) {
					e.printStackTrace();
				}
				mIsStartTagSended = true;
				sendPendingRequest();
				
				break;
				
			case REQUEST_APP_FINISH:
				Toast.makeText(mContext, "App finish tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("finish tag send success");
				break;
				
			case REQUEST_PURCHASE:
				Toast.makeText(mContext, "Purchase tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("purchase tag send success");
				break;
				
			case REQUEST_PAGE_CHANGE:
				Toast.makeText(mContext, "Page tag send success", Toast.LENGTH_SHORT).show();
				BKLog.d("page change tag send success");
				break;
				
			case REQUEST_WEB_TRACKING:
				Toast.makeText(mContext, "webView tracking send success(same as page change tag)", Toast.LENGTH_SHORT).show();
				BKLog.d("webView tracking send success(same as page change tag)");
				break;
				
		}
	}

	@Override
	public void onRequestError(int whichRequest, int errorCode, String message) {
		switch(whichRequest) {
			case REQUEST_APP_START:
				BKLog.e("start tag send fail");
				Toast.makeText(mContext, "App start tag send fail", Toast.LENGTH_SHORT).show();
				mIsStartTagSended = true;
				sendPendingRequest();
				break;
				
			case REQUEST_APP_FINISH:
				BKLog.e("finish tag send fail");
				Toast.makeText(mContext, "App finish tag send fail", Toast.LENGTH_SHORT).show();
				break;
				
			case REQUEST_PURCHASE:
				BKLog.e("purchase tag send fail");
				Toast.makeText(mContext, "Purchase tag send fail", Toast.LENGTH_SHORT).show();
				break;
				
			case REQUEST_PAGE_CHANGE:
				BKLog.e("page change tag send fail");
				Toast.makeText(mContext, "Page tag send fail", Toast.LENGTH_SHORT).show();
				break;
				
			case REQUEST_WEB_TRACKING:
				Toast.makeText(mContext, "webView tracking send fail(same as page change tag)", Toast.LENGTH_SHORT).show();
				BKLog.d("webView tracking send fail(same as page change tag)");
				break;
		}
	}
}
